package heap

import (
	"jvm/nzpJvm/classfile"
	"jvm/nzpJvm/runtime_area/heap/utils"
	"strings"
)

// 一个Class结构体就代表一个运行时类
// 这个结构体位于方法区。程序运行时，每加载一个class，都会生成一个Class结构体到方法区。
//当Class结构体被加载到方法区后，会在堆区生成一个类型为Class类型的java对象，作为访问此Class结构体的入口
// 注意Class结构体和Class类型的java对象是不一样的，一个在方法区，一个在堆区
// 这个结构体是不能直接在java代码里面访问到的，只能通过和它关联的Class类型的java对象来访问
type Class struct {
	accessFlags       uint16        // 这个类的访问标志
	name              string        // 这个类的类名
	superClassName    string        // 父类的类名
	interfaceNames    []string      // 实现的接口的名称列表
	constantPool      *ConstantPool // 运行时常量池索引
	fields            []*Field      // 字段
	methods           []*Method     // 方法
	loader            *ClassLoader  // 加载这个类的类加载器
	superClass        *Class        // 超类索引
	interfaces        []*Class      // 实现的接口索引
	instanceSlotCount uint          // 实例变量占据的空间大小
	staticSlotCount   uint          // 类变量占据的空间大小
	staticVars        Slots         // 存放静态变量

	initStarted bool // 是否初始化。意思是是否已经调用了类的初始化方法clinit

	javaClassObject *Object // 与此Class结构体关联的Class类型的java对象
}

func newClass(cf *classfile.ClassFile) *Class {
	class := &Class{}
	class.accessFlags = cf.AccessFlags()
	class.name = cf.ClassName()
	class.superClassName = cf.SuperClassName()
	class.interfaceNames = cf.InterfaceNames()
	class.constantPool = newConstantPool(class, cf.ConstantPool())
	class.fields = newFields(class, cf.Fields())
	class.methods = newMethods(class, cf.Methods())
	return class
}

// 检查这个类是否可以被另一个类访问到
func (self *Class) isAccessibleTo(other *Class) bool {
	// 如果类是public的，或者两个类在同一个运行时包内，就可以访问
	return self.IsPublic() || self.GetPackageName() == other.GetPackageName()
}

func (self *Class) GetPackageName() string {
	if i := strings.LastIndex(self.name, "/"); i >= 0 {
		return self.name[:i]
	}
	return ""
}

/**
判断一个类是不是另一个类的子类。用于判断protected访问权限
注意，如果self和class是同一个类，此方法返回false
*/
func (self *Class) IsSubClassOf(class *Class) bool {
	for c := self.superClass; c != nil; c = c.superClass {
		if c == class {
			return true
		}
	}
	return false

}
// 所有java类型的对象都对应一个Object结构体
func (self *Class) NewObject() *Object {
	return newObject(self)
}

/**
判断一个类是否能转换成另一个类
意思是self有没有继承other。或者如果other是一个接口，则判断self有没有实现other
*/
func (self *Class) IsAssignableFrom(other *Class) bool {
	if self == other {
		return true
	}
	//
	if !self.IsArray() {
		// 不是数组类
		if !self.IsInterface() {
			if !other.IsInterface() {
				// 当前类是一个class，另一个类也是一个class，看self是不是other的子类
				return self.IsSubClassOf(other)
			} else {
				// 当前类是一个class，另一个类是一个interface，看self及其父类有没有实现other
				return self.IsImplements(other)
			}
		} else {
			if !other.IsInterface() {
				// 当前类是一个接口,但是另一个类是一个class。
				// 那就看目标类是不是Object类
				return other == self.loader.LoadClass("java/lang/Object")
			} else {
				// 当前类是一个接口，另一个类也是一个接口，看有没有继承关系
				return other.IsSubInterfaceOf(self)
			}
		}
	} else {
		// 是数组类
		if !other.IsArray() {
			// 另一个类不是数组类
			if !other.IsInterface() {
				// Object类是数组类的父类
				return other == self.loader.LoadClass("java/lang/Object")
			} else {
				// 数组类实现了Cloneable和Serializable接口
				return other == self.loader.LoadClass("java/lang/Cloneable") || other == self.loader.LoadClass("java/io/Serializable")
			}
		} else {
			componentClass1 := self.ComponentClass()
			componentClass2 := other.ComponentClass()
			return componentClass1 == componentClass2 || componentClass2.IsAssignableFrom(componentClass1)
		}

	}
}

// 检查self类是否实现了指定的接口
func (self *Class) IsImplements(other *Class) bool {
	for c := self.superClass; c != nil; c = self.superClass {
		interfaces := c.interfaces
		for _, interfaceClass := range interfaces {
			if interfaceClass == other || interfaceClass.IsSubInterfaceOf(other) {
				return true
			}
		}
	}
	return false
}

func (self *Class) IsSubInterfaceOf(other *Class) bool {
	for _, superInterface := range self.interfaces {
		// 此接口就是目标接口或者此接口是目标接口的子接口
		if superInterface == other || superInterface.IsSubInterfaceOf(other) {
			return true
		}
	}
	return false
}

// 根据Class创建一个新的对象实例。这个方法仅用来创建普通对象，不创建数组对象
func newObject(class *Class) *Object {
	return &Object{
		// 此对象所属的类
		class: class,
		// 此对象中实例变量表
		data: newSlots(class.instanceSlotCount),
	}
}

// 判断flag标志
func (self *Class) IsPublic() bool {
	return 0 != self.accessFlags&ACC_PUBLIC
}
func (self *Class) IsFinal() bool {
	return 0 != self.accessFlags&ACC_FINAL
}
func (self *Class) IsSuper() bool {
	return 0 != self.accessFlags&ACC_SUPER
}
func (self *Class) IsInterface() bool {
	return 0 != self.accessFlags&ACC_INTERFACE
}
func (self *Class) IsAbstract() bool {
	return 0 != self.accessFlags&ACC_ABSTRACT
}
func (self *Class) IsSynthetic() bool {
	return 0 != self.accessFlags&ACC_SYNTHETIC
}
func (self *Class) IsAnnotation() bool {
	return 0 != self.accessFlags&ACC_ANNOTATION
}
func (self *Class) IsEnum() bool {
	return 0 != self.accessFlags&ACC_ENUM
}

// get方法
func (self *Class) AccessFlags() uint16 {
	return self.accessFlags
}
func (self *Class) Name() string {
	return self.name
}
func (self *Class) SuperClassName() string {
	return self.superClassName
}
func (self *Class) InterfaceNames() []string {
	return self.interfaceNames
}
func (self *Class) ConstantPool() *ConstantPool {
	return self.constantPool
}
func (self *Class) Fields() []*Field {
	return self.fields
}
func (self *Class) Methods() []*Method {
	return self.methods
}
func (self *Class) Loader() *ClassLoader {
	return self.loader
}
func (self *Class) SuperClass() *Class {
	return self.superClass
}
func (self *Class) Interfaces() []*Class {
	return self.interfaces
}
func (self *Class) InstanceSlotCount() uint {
	return self.instanceSlotCount
}
func (self *Class) StaticSlotCount() uint {
	return self.staticSlotCount
}
func (self *Class) StaticVars() Slots {
	return self.staticVars
}

func (self *Class) GetMainMethod() *Method {
	// 方法名称为main，并且参数是String数组
	return self.GetStaticMethod("main", "([Ljava/lang/String;)V")
}

// 从类中查找方法，没有去父类和父接口中查找
func (self *Class) GetStaticMethod(name string, description string) *Method {
	for _, method := range self.Methods() {
		if method.Name() == name && method.Descriptor() == description {
			return method
		}
	}
	return nil
}

func (self *Class)JavaClassObject() *Object {
	return self.javaClassObject
}

func (self *Class) InitStart() bool {
	return self.initStarted
}

func (self *Class) StartInit() {
	self.initStarted = true
}

func (self *Class) GetClinitMethod() *Method {
	// 方法名称为main，并且参数是String数组
	return self.GetStaticMethod("<clinit>", "()V")
}

func (self *Class) IsArray() bool {
	// 数组类的名称都是[开头
	return self.name[0] == '['
}

func (self *Class) ArrayClass() *Class {
	arrayClassName := getArrayClassName(self.name)
	// 加载指定的引用类型数组类
	return self.loader.LoadClass(arrayClassName)
}

func (self *Class) ComponentClass() *Class {
	// 返回数组的组件类型
	componentClassName := getComponentClassName(self.name)
	return self.loader.LoadClass(componentClassName)
}

func (self *Class) GetFieldByNameAndDesc(fieldName string, fieldDesc string, isStatic bool) *Field {
	// 遍历当前类及其所有父类
	for c := self; c != nil; c = self.superClass {
		classFields := c.fields
		// 查找指定的字段
		for _, classField := range classFields {
			if classField.name == fieldName && classField.descriptor == fieldDesc && classField.IsStatic() == isStatic {
				return classField
			}
		}
	}

	return nil
}

func getComponentClassName(name string) string {
	if name[0] == '[' {
		componentTypeDesc := name[1:]
		return toClassName(componentTypeDesc)
	}
	panic("no array" + name)
}

func toClassName(desc string) string {
	if desc[0] == '[' {
		// 是一个数组
		return desc
	}
	if desc[0] == 'L' {
		// 是一个对象。对象的描述格式是 Ljava/lang/Object;
		// 用L和;包裹的二进制类名
		return desc[1 : len(desc)-1]
	}
	for className, d := range utils.PrimitiveTypes {
		if d == desc {
			return className
		}
	}
	panic("invalid desc" + desc)
}

// 将class名称转换为对应的一维数组的名称
// 比如 java/lang/Object 对应的一维数组的名称为 [java/lang/Object
// I对应的一维数组名称为 [I
func getArrayClassName(name string) string {
	return "[" + toDescription(name)
}

func toDescription(name string) string {
	if name[0] == '[' {
		return name
	}
	// 从map中获取元素，第一个返回值是键对应的值，如果键不存在，这个返回值是值的类型对应的零值
	// 第二个返回值标识键是否真的存在于map中
	// 如果类的类型是基本类型，则原样返回
	if d, ok := utils.PrimitiveTypes[name]; ok {
		return d
	}
	// jvm规范。引用类型，以L开头，以分号结尾。中间的内容才是引用类型的名称
	return "L" + name + ";"
}
